package com.cwx.mychat.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cwx.mychat.entity.constant.CodeConstant;
import com.cwx.mychat.entity.dto.TokenUserInfoDto;
import com.cwx.mychat.entity.enums.ExceptionBizCodeEnum;
import com.cwx.mychat.entity.enums.FileStatusEnum;
import com.cwx.mychat.entity.po.FileCloud;
import com.cwx.mychat.exception.BusinessException;
import com.cwx.mychat.mappers.FileCloudMapper;
import com.cwx.mychat.service.FileCloudService;
import com.cwx.mychat.utils.R;
import com.cwx.mychat.utils.common.CommonUtil;
import com.cwx.mychat.utils.minio.MinioUtil;
import com.cwx.mychat.utils.stringUtils.StringUtil;
import io.minio.ObjectStat;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 作者： cwx
 * 时间： 2025/5/21 16:54
 * 版本： 1.0
 * 注释：
 */
@Slf4j
@Service
public class FileCloudServiceImpl extends ServiceImpl<FileCloudMapper, FileCloud> implements FileCloudService {


    @Autowired
    private FileCloudMapper fileCloudMapper;

    @Autowired
    private MinioUtil minioUtil;


    @Override
    public R list(String userId, String parentId) {
        //获取列表并按照时间进行排序,不查询filePath
        List<FileCloud> fileClouds = fileCloudMapper.selectList(new LambdaQueryWrapper<FileCloud>()
                .eq(FileCloud::getUserId, userId)
                .eq(FileCloud::getParentId, parentId)
                .eq(FileCloud::getFileStatus, FileStatusEnum.NORMAL.getStatus())
                .select(FileCloud.class, i -> !i.getColumn().equals("file_path"))
                .orderByDesc(FileCloud::getCreateTime));
        //文件夹需要再最上面，文件在后面排
        fileClouds.sort((o1, o2) -> o1.getFileType().equals("folder") ? -1 : 1);
        return R.ok().put(CodeConstant.RESULT_MSG, fileClouds);
    }

    @Override
    public R upload(MultipartFile file, String userId, String parentId, String userName, String fileType, String state) throws BusinessException {

        String fileSuffix = StringUtil.getFileSuffix(file.getOriginalFilename());
        if (StringUtil.isEmpty(fileSuffix)){
            log.error("上传文件格式错误， 后缀为空");
            throw new BusinessException(ExceptionBizCodeEnum.FILE_UPLOAD_FAILED);
        }
        //保存到数据库和minio中
        String imageUrl = minioUtil.uploadFile(file, fileType + "-cloud");
        FileCloud fileCloud = new FileCloud();
        //获取文件大小
        fileCloud.setFileSize(file.getSize());
        fileCloud.setFileName(file.getOriginalFilename());
        fileCloud.setUserId(userId);
        fileCloud.setParentId(parentId);
        fileCloud.setUserName(userName);
        fileCloud.setCreateTime(new Date());
        fileCloud.setFileStatus(FileStatusEnum.NORMAL.getStatus());
        fileCloud.setFileType(fileType);
        fileCloud.setUpdateTime(new Date());
        fileCloud.setState(state);
        fileCloud.setFilePath(imageUrl);

        //保存到数据库
        fileCloudMapper.insert(fileCloud);
        return R.ok();
    }

    @Override
    public R delete(String fileId, String userId) throws BusinessException {

        //查询该文件是否是该人创建的文件
        FileCloud fileCloud = fileCloudMapper.selectOne(new LambdaQueryWrapper<FileCloud>()
                .eq(FileCloud::getFileId, fileId).eq(FileCloud::getUserId, userId));
        if (fileCloud == null) {
            log.error("删除文件失败，文件不存在");
            throw new BusinessException(ExceptionBizCodeEnum.FILE_NOT_MATCH_USER);
        }
        //删除minio中文件
        boolean b = minioUtil.deleteFile(fileCloud.getFilePath());
        if(!b){
            log.error("删除文件失败，minio中文件不存在");
            throw new BusinessException(ExceptionBizCodeEnum.FILE_NOT_EXIST);
        }
        //删除数据库文件
        fileCloudMapper.deleteById(fileCloud.getFileId());
        return R.ok();
    }

    @Override
    public R deleteFolder(String folderId, String userId) throws BusinessException {
        TokenUserInfoDto tokenUserInfoDto = CommonUtil.getSecurityContext();
        if(!tokenUserInfoDto.getUserId().equals(userId)){
            return R.error("用户无权限删除");
        }
        //查询文件夹是否存在
        FileCloud fileCloud = fileCloudMapper.selectOne(new LambdaQueryWrapper<FileCloud>()
                .eq(FileCloud::getFileId, folderId).eq(FileCloud::getUserId, userId));
        if (fileCloud == null) {
            log.error("删除文件夹失败，文件夹不存在");
            throw new BusinessException(ExceptionBizCodeEnum.FILE_NOT_EXIST);
        }

        //递归获取文件及文件夹
        List<FileCloud> fileClouds = fileCloudMapper.selectList(new LambdaQueryWrapper<FileCloud>()
                .eq(FileCloud::getParentId, folderId).eq(FileCloud::getUserId, userId));
        List<FileCloud> result = new ArrayList<>(fileClouds);
        //将当前文件放进result中
        result.add(fileCloud);
        getFileList(userId, fileClouds, result);
        //批量删除minio文件
        List<String> fileUrls = new ArrayList<>();
        List<String> fileIds = new ArrayList<>();
        result.forEach(item -> {
            fileUrls.add(item.getFilePath());
            fileIds.add(item.getFileId());
        });
        if(!minioUtil.deleteFiles(fileUrls)) return R.error("删除文件夹失败");
        //数据库删除
        int i = fileCloudMapper.deleteByIds(fileIds);
        if(i != result.size()) return R.error("删除文件夹失败");
        return R.ok();
    }

    //递归查询文件夹以及文件
    private void getFileList(String userId, List<FileCloud> fileClouds, List<FileCloud> result) {
        //获取id
        fileClouds.forEach(item -> {
            List<FileCloud> fileClouds1 = fileCloudMapper.selectList(new LambdaQueryWrapper<FileCloud>()
                    .eq(FileCloud::getParentId, item.getFileId()).eq(FileCloud::getUserId, userId));
            result.addAll(fileClouds1);
            getFileList(userId, fileClouds1, result);
        });
    }

    @Override
    public ResponseEntity<InputStreamResource> download(String fileId, String userId, HttpServletResponse response) throws BusinessException {
        InputStream inputStream = null;
        try{
            //查询文件是否存在
            FileCloud fileCloud = fileCloudMapper.selectOne(new LambdaQueryWrapper<FileCloud>()
                    .eq(FileCloud::getFileId, fileId).eq(FileCloud::getUserId, userId));
            if (fileCloud == null) {
                log.error("下载文件失败，文件不存在");
                throw new BusinessException(ExceptionBizCodeEnum.FILE_NOT_EXIST);
            }
            //下载文件
            String filePath = fileCloud.getFilePath();
            if(filePath == null){
                throw new BusinessException(ExceptionBizCodeEnum.FILE_URL_EXIST);
            }
            String urlFileName = StringUtil.getUrlFileName(filePath);
            //检查文件是否存在
            ObjectStat objectStat = minioUtil.checkFileExits(urlFileName);
            if(objectStat == null){
                throw new BusinessException(ExceptionBizCodeEnum.MINIO_FILE_EXIST);
            }
            inputStream = minioUtil.downloadFile(urlFileName);
            if(inputStream == null){
                throw new BusinessException(ExceptionBizCodeEnum.MINIO_FILE_EXIST);
            }
            ResponseEntity<InputStreamResource> body = ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + URLEncoder.encode(urlFileName, StandardCharsets.UTF_8) + "\"")
                    .header(HttpHeaders.CONTENT_TYPE, objectStat.contentType())
                    .header(HttpHeaders.CONTENT_LENGTH, String.valueOf(objectStat.length()))
                    .body(new InputStreamResource(inputStream));
            return body;
        }catch (Exception e){
            log.error("下载文件失败", e);
            if(inputStream != null){
                try{
                    inputStream.close();
                }catch (Exception e1){
                    log.error("关闭文件流失败", e1);
                }
            }
            throw new BusinessException(ExceptionBizCodeEnum.FILE_DOWNLOAD_ERROR);
        }
    }
}
